<?php if (!defined('CORE_DIR')) die('Wrong entry point!');

/* 
 * Copyright 2010 Anton Muraviev a.k.a kuromaisu
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

Aero::define('CoreSpecification', AS_SINGLETON);

/*
 * CoreSpecification
 */
class CoreSpecification extends AeroSpec
{
	
	protected function itShouldReturnDefaultValueForUnsetOptions()
	{
		$def_value = '__default_value__';
		$value = Aero::option('some-namespace.some_value',$def_value);
		
		$this->variable($value)->equals($def_value);
	}

	protected function itShouldSetAndUnsetOptions()
	{
		// Set
		$set_value = 'options_set';
		Aero::setOption('some-namespace.some_value',$set_value);
		$value = Aero::option('some-namespace.some_value');
		
		$this->variable($value)->equals($set_value);
		
		// Unset
		Aero::unsetOption('some-namespace.some_value');
		$value = Aero::option('some-namespace.some_value');
		$this->variable($value)->doesNotEqual($set_value);
	}
	
	protected function itShouldSetMultipleOptionsFromAnArray()
	{
		$ns = Aero::option('some-namespace',null);
		
		$this->variable($ns)->isIdenticalTo(null);
		
		$options = array(
			'option_1' => 'value_1',
			'option_2' => 'value_2',
			'option_3' => 'value_3'
		);
		Aero::setOption('some-namespace',$options);
		
		$option = Aero::option('some-namespace.option_1');
		$this->variable($option)->equals($options['option_1']);
		
		$options_2  = array(
			'another.namespace.option_1' => 'value_1',
			'another.namespace.option_2' => 'value_2',
			'another.namespace.option_3' => 'value_3',
		);
		Aero::setOption($options_2);
		$this->value(Aero::option('another.namespace.option_2'))->equals('value_2');
	}

	protected function itShouldReturnAllOptionsInTheNamespace()
	{
		$options = array(
			'option_1' => 'value_1',
			'option_2' => 'value_2',
			'option_3' => 'value_3'
		);
		Aero::setOption('some-namespace',$options);
		
		$this->variable($options)->isIdenticalTo(Aero::option('some-namespace'));
	}
	
	protected function itShouldUseDefaultValuesIfSomeOptionsInTheNamespaceAreMissing()
	{
		$option = array(
			'option_1' => 'value_1',
			'option_2' => 'value_2',
			'option_3' => 'value_3'
		);
		$default = $option + array('option_4' => 'value_4');
		
		Aero::setOption('some-namespace.array',$option);
		
		$result = Aero::option('some-namespace.array',$default);
		
		$this->dictionary($result)->valueOf('option_4')->equals($default['option_4']);
	}
	
	protected function itShouldSetAndResetScalarValuesCorrectly()
	{
		Aero::setOption('some-namespace.scalar_option','option_1');
		$this->value(Aero::option('some-namespace.scalar_option'))->equals('option_1');
		
		Aero::setOption('some-namespace.scalar_option','option_2');
		Aero::setOption('some-namespace.scalar_option','option_3');
		
		$this->value(Aero::option('some-namespace.scalar_option'))->equals('option_3');
	}
	
	protected function afterEach()
	{
		Aero::unsetOption(array('some-namespace','another'));
	}
	
	protected function itShouldNormalizeFilePaths()
	{
		$path_1 = '\some\path';
		$path_2 = '\some\path/';
		$path_3 = 'some\very\long\path/to\directory/';
		
		$this->value(aero_trim_path($path_1))->equals('some/path');
		$this->value(aero_trim_path($path_2))->equals('some/path');
		$this->value(aero_trim_path($path_3))->equals('some/very/long/path/to/directory');
	}
	
	protected function itShouldImportFiles()
	{
		// Simple library load
		Aero::import('specs/core/test_lib_1');
		
		$this->isTrue(Aero::isImported('specs/core/test_lib_1'));
		$this->isFalse(Aero::isInitialized('specs/core/test_lib_1'));
	}
	
	protected function itShouldThrowExceptionWhenLoadingMissingFiles()
	{
		$this->throws('AeroException');
		Aero::import('specs/core/non_existant_lib');
	}
	
	protected function itShouldThrowExceptionWhenRetrievingInfoAboutMissingFiles()
	{
		$this->throws('AeroException');
		Aero::classNameOf('specs/core/non_existant_lib');
	}
	
	protected function itShouldReturnInformationAboutImportedFiles()
	{
		$class_name = Aero::classNameOf('specs/core/test_lib_1');
		
		$this->variable($class_name)->equals('TestLibrary');
	}
	
	protected function itShouldCorrectlyRestoreInternalStacksIfExceptionIsThrown()
	{
		try {
			Aero::run('specs/core/test_exception_script');
		} catch (Exception $e) {
			// do nothing
		}
		
		$this->value(Aero::classNameOf('specs/core/test_exception_lib'))->equals('TestExceptionLib');
		$this->value(Aero::classNameOf('specs/core/test_exception_lib_2'))->equals('TestExceptionLib2');
		$this->value(Aero::classNameOf('specs/core/test_exception_lib_3'))->equals('TestExceptionLib3');
		
		$obj = Aero::load('specs/core/test_exception_lib_2');
		
		$this->object($obj)->isInstanceOf('TestExceptionLib2');
	}
	
	protected function itShouldLoadNormalLibraries()
	{
		$lib = Aero::load('specs/core/test_normal_lib');
		$lib2 = Aero::load('specs/core/test_normal_lib');
		
		$this->variable($lib)->is('object');
		$this->variable($lib2)->is('object');
		
		// Only singletons are initialized by Aero
		$this->isFalse(Aero::isInitialized('specs/core/test_normal_lib'));
	}

	protected function itShouldLoadSingletonLibraries()
	{
		Aero::import('specs/core/test_singleton_lib');
		$lib = Aero::load('specs/core/test_singleton_lib');
		$lib2 = Aero::load('specs/core/test_singleton_lib');
		
		$this->isTrue(Aero::isInitialized('specs/core/test_singleton_lib'));
		$this->variable($lib)->is('object')->references($lib2);
	}

	protected function itShouldLoadStaticLibraries()
	{
		$lib = Aero::load('specs/core/test_static_lib');
		
		$this->isFalse(Aero::isInitialized('specs/core/test_static_lib'));
		$this->variable($lib)->is(null);
		$this->isTrue(class_exists('TestStaticLibrary'));
	}
	
	protected function itShouldAddNewContextAndRestoreToPreviousOneWhenFinished()
	{
		$testContext = Aero::load('specs/core/test_context');
		$this->variable($testContext)->is('object');
		
		$lib = $testContext->foo();
		$this->object($lib)->isInstanceOf('TestLibraryForContext');
	}

	protected function itShouldBeAbleToCaptureOutputAndReturnItAsAString()
	{
		$output = Aero::run('specs/core/test_script',array(),true);
		$this->string($output)->isSimilarTo('Some simple output!');
	}
	
	protected function itShouldRunExternalScriptsPassingParametersToThem()
	{
		$output = Aero::run('specs/core/test_script',array('test_var' => '1', 'test_var_2' => '!'),true);
		$this->string($output)->isSimilarTo('Some simple output!1+!');
	}
	
	protected function itShouldBeAbleToOutputExecutedScript()
	{
		ob_start();
		Aero::run('specs/core/test_script');
		$output = ob_get_clean();
		
		$this->string($output)->isSimilarTo('Some simple output!');
	}
}
